-- TestFightM
-- created by cheny3
-- 自动战斗测试

module("TestFightM", package.seeall);


local TEST_START = 1;
local TEST_STOP  = 2;
local syncErrorTimes = {0, 0};

-- 一开始是停止状态的
local state = TEST_STOP;

local careers = {};
local spells = {};
local properties = {};
local equips = {};
local test_fight = {};
local specialLog = {};
local logName;

-- 当前测试用例ID
local test_id;

-- 模块初始化操作
function init()
    if DEBUG_MODE == true or DEBUG_MODE == 1 then
        -- 载入配置表
        loadTable();

        -- 注册事件
        registerEvent();
    end
end

-- 载入配置表
local _init = false;
function loadTable()
    if _init then return end
    _init = true;
    
    -- 转换下career信息表
    local career_CSV = get_career_CSV();
    for i = 1, #career_CSV do
        careers[career_CSV[i].id] = career_CSV[i];
    end
    career_CSV = {};

    -- 转换下spell信息表
    local spell_CSV = get_spell_CSV();
    for i = 1, #spell_CSV do
        spells[spell_CSV[i].class_id] = spell_CSV[i];
    end
    spell_CSV = {};

    -- 转换下property信息表
    local property_CSV = get_property_CSV();
    for i = 1, #property_CSV do
        properties[property_CSV[i].class_id] = property_CSV[i];
    end
    property_CSV = {};

    -- 转换下equip信息表
    local equip_CSV = get_equip_CSV();
    for i = 1, #equip_CSV do
        local classId = equip_CSV[i].class_id;
        equips[classId] = equip_CSV[i];
    end
    equip_CSV = {};

    -- 测试战斗配置
    local test_fight_CSV = get_test_fight_CSV();
    for i = 1, #test_fight_CSV do
        test_fight[test_fight_CSV[i].id] = test_fight_CSV[i];
    end
    test_fight_CSV = {};
end

-- 注册事件
function registerEvent()
    EventMgr.removeAll("TestFightM");
    -- 关注登录“在地牢中”消息
    EventMgr.register("TestFightM", event.MESSAGE_DONE, function(para)
        handleMessageDone(para);
    end);

    -- 关注玩家死亡
    EventMgr.register("TestFightM", event.HERO_DIE, function(para)
        if getState() ~= TEST_START then
            return;
        end

        EventMgr.fire(event.AUTO_FIGHT_MOVE, {});
        local reviveTimes = ME.user:getReviveTimes();
        local info = test_fight[test_id] or {};
        local maxRevive = info.max_revive or 5;
        if reviveTimes > maxRevive then
            -- 记录
            logDungeonInfo("combatStat:\n" .. tableToString(CombatStatM.getStatInfo()));
            logDungeonInfo("testLog:\n" .. tableToString(getSpecialLog()));
            -- 离开迷宫
            EventMgr.fire(event.AUTO_FIGHT_LEAVE, {});
            return;
        end

        local schedulerId;
        local function delay()
            cc.Director:getInstance():getScheduler():unscheduleScriptEntry(schedulerId);

            -- 执行复活
            if DungeonActionM.go("revive") then
                -- 重新开始
                AutoFightM.start();
            else
                -- 离开迷宫
                EventMgr.fire(event.AUTO_FIGHT_LEAVE, {});
            end
        end
        schedulerId = cc.Director:getInstance():getScheduler():scheduleScriptFunc(delay, 2, false);
    end);

    -- 神龙许愿
    EventMgr.register("TestFightM", event.WISH_TRIGGER, function(para)
        if getState() ~= TEST_START then
            return;
        end

        if para.isOpenForm then
            local schedulerId;
            local function delay()
                -- 神龙许愿
                EventMgr.fire(event.AUTO_FIGHT_MOVE, {});
                -- 三个选项随机选一个
                local index = math.random(1, 3);
                DungeonActionM.go("do_dragon_wish", 0, index);
                cc.Director:getInstance():getScheduler():unscheduleScriptEntry(schedulerId);
                -- 重新开始
                AutoFightM.start();
            end
            schedulerId = cc.Director:getInstance():getScheduler():scheduleScriptFunc(delay, 2, false);
        end
    end);

    -- 开始新的一层
    EventMgr.register("TestFightM", event.DUNGEON_AREADY, function()
        if getState() ~= TEST_START then
            return;
        end

        -- 调用自动战斗
        -- AutoFightM.setPauseTime(os.time() + 2);
        -- AutoFightM.start();
    end);

    -- 离开迷宫
    EventMgr.register("TestFightM", event.LEAVE_DUNGEON, function()
        if getState() ~= TEST_START then
            return;
        end

        -- 记录
        logDungeonInfo("end_dungeon");

        -- 删除当前测试数据
        test_id = nil;
    end);
end

-- 获取标记
function getState()
    return state;
end

-- 是否在测试流程
function isInTest()
    return state == TEST_START;
end

-- 测试流程开始
function startTestFight(id)
    -- 设置标示
    state = TEST_START;

    -- 如果有传入ID
    if id then
        test_id = id;
        -- 设置模板信息
        local info = test_fight[id];
        local template = info.template;
        AutoFightM.setTemplate(template);
    end
end

-- 获得当前测试信息
function getTestInfo()
    if state ~= TEST_START or not test_id then
        return;
    end
    return test_fight[test_id];
end

-- 测试流程结束
function stopTestFight()
    state = TEST_STOP;
    -- 停止自动战斗
    AutoFightM.stop();
end

-- 设置灵魂链接
function setLictorPets(id)
    -- 模板信息
    local info = test_fight[id];

    -- 出战宠物
    if PetM.getMyPet(info["active_pet"]) then
        PetM.setActivePet(info["active_pet"]);
    end

    -- 激活中的宠物
    local activeId = ME.user.dbase:query("active_pet");

    -- 灵魂链接数据
    local pets;
    if type(info.lictor) == "table" then
        -- 有配置
        pets = info.lictor;
    else
        -- 默认全链接
        pets = CampM.getCampPets(activeId);
    end

    -- 设置灵魂链接
    local petIds = {};
    for _, classId in pairs(pets) do
        if PetM.getMyPet(classId)
            and classId ~= activeId then
            table.insert(petIds, classId);
        end
    end

    ME.user.dbase:set("lictor_pets", petIds);
    -- 刷新下属性
    PropM.refresh(ME.user);
end

-- 收到测试开始消息
function startTestDungeon(id)
    -- 灵魂链接等信息由服务器下发
    if DungeonM.isInDungeon() then
        -- 已经在迷宫里了
        AutoFightM.start();
        return;
    end

    -- 当前测试用例
    local info = test_fight[id];
    test_id = id;

    -- 设置灵魂链接
    setLictorPets(id);

    -- 设置神器
    if type(info.artifact) == "number" then
        ArtifactM.carryArtifact(info.artifact);
    end

    -- 设置流程开始
    startTestFight();

    -- 设置模板信息
    local template = info.template;
    AutoFightM.setTemplate(template);

    -- 清空记录
    specialLog = {};
    syncErrorTimes = {0, 0};

    -- 创建一个新的文件
    logName = createDungeonFile();

    -- 开始迷宫
    local dungeonId = info.dungeon_id;
    DungeonServiceM.startEnterService(dungeonId);

    -- 记录
    logDungeonInfo("start_dungeon");
end

-- 记录
function setSpecialLog(path, info)
    specialLog[path] = info;
end

-- 获取记录
function getSpecialLog(path)
    if path then
        return specialLog[path];
    end
    return specialLog;
end

-- 进入迷宫给玩家道具
function giveAllProperty()
    -- 全给
    local classId;
    local bonusNum = 1;
    for _, classId in pairs(table.keys(properties)) do
        if ItemM.query(classId) then
            -- 使用GD接口
            GDM.add_item(classId, bonusNum);
        end
    end
end

-- 进入迷宫给玩家奖励卷轴
function giveAllSpells()
    -- 全给
    local classId;
    local bonusNum = 999;
    for _, classId in pairs(table.keys(spells)) do
        if ItemM.query(classId) then
            -- 使用GD接口
            GDM.add_item(classId, bonusNum);
        end
    end
end

-- 玩家当前有的所有卷轴
function getUserSpells()
    local ret = {};
    for _, classId in pairs(table.keys(spells)) do
        if ItemM.query(classId) and ItemM.getAmount(ME.user, classId) > 0 then
            table.insert(ret, classId);
        end
    end
    return ret;
end

-- 进入迷宫给玩家奖励装备
function giveAllEquips()
    -- 全给
    local classId;
    local bonusNum = 1;
    for _, classId in pairs(table.keys(equips)) do
        local info = EquipM.query(classId);
        if table.indexOf(bonusType, info["type"]) ~= -1 and ItemM.query(classId) then
            -- 使用GD接口
            GDM.add_item(classId, bonusNum);
        end
    end
end

-- 设置全称号学习
function setPetCareer()
    local pet = ME.user:getActivePet();
    local petCareers = {};

    -- 遍历所有称号
    for _, careerInfo in pairs(careers) do
        -- 系别
        local department = careerInfo["department"];

        -- 当前某系别称号信息
        local departmentInfos = petCareers[department] or {};

        -- 称号阶
        local rank =  careerInfo["rank"];
        -- 最大等级
        local maxLevel = careerInfo["max_level"];
        -- 当前INDEX
        local index = #departmentInfos + 1;

        if rank >= 5 then
            -- 五阶有三个子类
            departmentInfos[index] = {
                ["feature"] = {[1] = 3, [2] = 3, [3] = 3},
                ["career"] = careerInfo["id"],
                ["level"] = 10,
            };
        else
            departmentInfos[index] = {
                ["feature"] = {[1] = maxLevel},
                ["career"] = careerInfo["id"],
                ["level"] = maxLevel,
            };
        end

        petCareers[department] = departmentInfos;
    end

    pet:updateCareerData(petCareers);
    return true;
end

-- 获取子模块
function getModule(ruleName)
    local rule = rules[ruleName];
    if rule == "TBL" then
        rules[ruleName] = LOAD_RUN("game/logic/module/test_fight/" .. ruleName);
        return rules[ruleName];
    end

    return rule;
end

-- 处理玩家消息
function handleMessageDone(para)
    if state ~= TEST_START then
        return;
    end
    if para[1] == "MSG_IN_DUNGEON" then
        -- TODO
    elseif para[1] == "MSG_END_SYNC" and para[2].result == 0 then
        -- 数据异常
        -- 暂停自动战斗
        AutoFightM.pause();
        -- 记录
        logSyncError();
        -- 备注：自动重新登陆相关在UIMainMap里执行

        -- 记录本层数据异常次数
        local layer = DungeonM.currentLayer();
        if syncErrorTimes[1] == layer then
            syncErrorTimes[2] = syncErrorTimes[2] + 1;
        else
            syncErrorTimes[1] = layer;
            syncErrorTimes[2] = 1;
        end
    elseif para[1] == "MSG_ENTER_WORLD" then
        -- 获取到MSG_ENTER_WORLD后才允许操作
        closeCommunicatingForm();
        -- 先暂停
        AutoFightM.pause();

        if syncErrorTimes[2] >= 5 then
            -- 已经同层反复数据异常超过5次
            -- 记录
            logDungeonInfo("combatStat:\n" .. tableToString(CombatStatM.getStatInfo()));
            logDungeonInfo("testLog:\n" .. tableToString(getSpecialLog()));
            -- 直接离开迷宫
            EventMgr.fire(event.AUTO_FIGHT_LEAVE, {});
            logSyncError("同层数据异常超过5次。");
            return;
        end

        -- 直接进迷宫
        local currentDungeon = ME.user.dbase:query("currentDungeon");
        if currentDungeon ~= nil then
            showCommunicatingForm();
            DungeonAreaM.setCurrentArea(ME.user, currentDungeon);
            DungeonServiceM.startEnterService(currentDungeon);
        end
    end
end

-- 如果显示无网络连接
function noDungeonInfo()
    -- TODO临时解决方法
    logSyncError("客户端显示无网络连接。");

    -- 暂停
    AutoFightM.pause();
    -- 暂离一下再进入
    EventMgr.fire(event.AUTO_FIGHT_PAUSE, {});
    if DungeonM.isInDungeon() or DungeonM.isPause() then
        -- 暂离状态，直接进入
        local id = ME.user.dbase:query("current_dungeon");
        DungeonServiceM.startEnterService(id);
    end
end

-- 以下是记录相关

local DIR_NAME = "test";
local ERROR_PATH = "test/error.txt";
local TEST_LOG_PATH = "test/test_log.txt";
local SYNC_ERROR_LOG_PATH = "test/sync_error.txt";

-- 创建一个新的文件名字
function createDungeonFile()
    -- 直接用时间戳
    local name = os.time();
    return name;
end

-- 记录数据
function saveDungeonLog()

end

-- 记录客户端错误
function logError(content)
    if not isInTest() then
        return;
    end

    -- 先试图创建一个文件夹
    if not file.isDirectoryExist(DIR_NAME) then
        file.createDirectory(DIR_NAME);
    end

    -- 添加错误记录
    content = content .. "\n";
    file.updateFile(content, ERROR_PATH);
end

-- 记录测试信息
function logDungeonInfo(logType)
    if not isInTest() then
        return;
    end

    -- 先试图创建一个文件夹
    if not file.isDirectoryExist(DIR_NAME) then
        file.createDirectory(DIR_NAME);
    end

    -- TODO详细的需要记录的信息
    local detailInfo = "";
    local info = test_fight[test_id];
    if not info then
        return;
    end

    if logType == "start_dungeon" then
        detailInfo = string.format("玩家开始迷宫：测试ID：%d/迷宫ID：%d\n", test_id, info["dungeon_id"]);
    elseif logType == "end_dungeon" then
        detailInfo = string.format("玩家结束迷宫：测试ID：%d/迷宫ID：%d\n", test_id, info["dungeon_id"]);
    else
        detailInfo = logType;
    end

    local info = "";
    info = info .. "------------" .. os.date() .. "----------\n";
    info = info .. detailInfo;
    info = info .."\n\n";

    -- 添加信息
    file.updateFile(info, TEST_LOG_PATH);
end

-- 记录数据异常信息
function logSyncError(detail)
    if not isInTest() then
        return;
    end

    if not file.isDirectoryExist(DIR_NAME) then
        file.createDirectory(DIR_NAME);
    end

    -- TODO
    local syncErrorDesc = string.format("数据异常（迷宫：%d）", tonumber(DungeonM.getDungeonId()));
    local detailInfo = detail or syncErrorDesc;

    -- 客户端简单记录收到数据异常
    local info = "";
    info = info .. "------------" .. os.date() .. "----------\n";
    info = info .. detailInfo;
    info = info .. "\n----------------------------------------\n\n";

    -- 记录数据异常
    file.updateFile(info, SYNC_ERROR_LOG_PATH);
end
